/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-nested-ternary */
import { useControlController, useNamespace } from '@ibiz-template/vue3-util';
import { computed, defineComponent, PropType, ref } from 'vue';
import { IDETree } from '@ibiz/model-core';
import {
  IControlProvider,
  ITreeNodeData,
  TreeController,
} from '@ibiz-template/runtime';
import {
  findNodeData,
  formatNodeDropType,
  getActiveNodeControlPanel,
  getNodeControlPanel,
} from './odoo-org-tree-util';
import RecursiveTree from './recursive-tree/recursive-tree';
import { AllowDropType, NodeDropType } from './recursive-tree-util';
import './odoo-org-tree.scss';

export const OdooOrgTreeControl = defineComponent({
  name: 'IBizOdooOrgTreeControl',
  props: {
    /**
     * @description 树模型数据
     */
    modelData: { type: Object as PropType<IDETree>, required: true },
    /**
     * @description 应用上下文对象
     */
    context: { type: Object as PropType<IContext>, required: true },
    /**
     * @description 视图参数对象
     * @default {}
     */
    params: { type: Object as PropType<IParams>, default: () => ({}) },
    /**
     * @description 部件适配器
     */
    provider: { type: Object as PropType<IControlProvider> },
    /**
     * @description 树节点默认激活模式，值为0:不激活，值为1：单击激活，值为2：双击激活
     */
    mdctrlActiveMode: { type: Number, default: undefined },
    /**
     * @description 是否单选
     */
    singleSelect: { type: Boolean, default: undefined },
    /**
     * @description 是否是导航的
     */
    navigational: { type: Boolean, default: undefined },
    /**
     * @description 默认展开节点集合
     */
    defaultExpandedKeys: { type: Array as PropType<string[]> },
    /**
     * @description 是否默认加载数据
     * @default true
     */
    loadDefault: { type: Boolean, default: true },
    /**
     * @description 在显示复选框的情况下，是否严格的遵循父子不互相关联的做法
     * @default true
     */
    checkStrictly: { type: Boolean, default: true },
    /**
     * @description 是否是简单模式
     * @default false
     */
    isSimple: { type: Boolean, default: false },
    /**
     * @description 简单模式下传入的数据
     */
    data: { type: Array<ITreeNodeData>, required: false },
  },
  setup(props) {
    const c = useControlController<TreeController>(
      (...args) => new TreeController(...args),
    );
    const ns = useNamespace(`odoo-org-tree`);

    const treeRef = ref();

    /** 树展示数据 */
    const treeData = computed(() => {
      if (!c.state.isLoaded) {
        return [];
      }
      return c.model.rootVisible
        ? c.state.rootNodes
        : c.state.rootNodes.reduce<ITreeNodeData[]>((result, nodeData) => {
            if (nodeData._children) {
              return result.concat(nodeData._children as ITreeNodeData[]);
            }
            return result;
          }, []);
    });

    /**
     * 创建新的节点对象，隔离组件数据和controller数据
     * @param {ITreeNodeData[]} nodes
     * @return {*}  {IData[]}
     */
    const toElNodes = (nodes: ITreeNodeData[]): IData[] => {
      return nodes.map(node => ({
        _id: node._id,
        _uuid: node._uuid,
        _leaf: node._leaf,
        _text: node._text,
        _disableSelect: node._disableSelect,
      }));
    };

    /**
     * 触发节点加载数据
     * @param {IData} item
     * @param {(nodes: IData[]) => void} callback
     */
    const loadData = async (
      item: IData,
      callback: (nodes: IData[]) => void,
    ): Promise<void> => {
      let nodes: ITreeNodeData[];
      if (item.level === 0) {
        nodes = treeData.value as ITreeNodeData[];
        ibiz.log.debug('初始加载');
      } else {
        const nodeData = findNodeData(item.data._id, c)!;
        if (nodeData._children) {
          ibiz.log.debug('节点展开加载-本地', nodeData);
          nodes = nodeData._children;
        } else {
          ibiz.log.debug('节点展开加载-远程', nodeData);
          nodes = await c.loadNodes(nodeData);
        }
      }

      callback(toElNodes(nodes));
    };

    const handleExpanded = (_node: IData): void => {
      if (treeRef.value) treeRef.value?.handleNodeExpand(_node);
    };

    const handleRefresh = (): void => {
      c.refresh();
      if (treeRef.value) {
        treeRef.value.refresh();
      }
    };

    /**
     * 处理面板项事件
     *
     * @param {IData} _args
     * @return {*}
     */
    const onPanelItemEvent = (_args: IData, _node: IData): void => {
      if (!_args) return;
      const { panelItemEventName } = _args;
      switch (panelItemEventName) {
        case 'onExpanded':
          handleExpanded(_node);
          break;
        case 'onRefresh':
          handleRefresh();
          break;
        default:
      }
    };

    /**
     * 节点单击事件
     */
    let forbidClick = false;
    const onNodeClick = (
      nodeData: ITreeNodeData,
      data: IData,
      evt: MouseEvent,
    ): void => {
      evt.stopPropagation();
      if (nodeData._disableSelect || forbidClick) return;
      if (props.isSimple) {
        treeRef.value!.setCurrentKey(data?._id || undefined);
      } else {
        c.onTreeNodeClick(nodeData, evt);
      }
      forbidClick = true;
      setTimeout(() => {
        forbidClick = false;
      }, 200);
    };

    /**
     * 节点双击事件
     */
    const onNodeDbClick = (nodeData: ITreeNodeData, evt: MouseEvent): void => {
      evt.stopPropagation();
      if (nodeData._disableSelect) return;
      c.onDbTreeNodeClick(nodeData);
    };

    /**
     * 是否是循环
     *
     * @param {IData} draggingNodeData
     * @param {IData} dropNodeData
     */
    const isCyclical = (
      draggingNodeData: ITreeNodeData,
      dropNodeData: ITreeNodeData,
    ): boolean => {
      if (draggingNodeData._id === dropNodeData._id) {
        return true;
      }
      if (dropNodeData._parent) {
        return isCyclical(draggingNodeData, dropNodeData._parent);
      }
      return false;
    };

    // 拖拽相关
    const allowDrop = (
      draggingNode: IData,
      dropNode: IData,
      type: AllowDropType,
    ): boolean => {
      if (dropNode.data?._load_more) {
        return false;
      }

      const draggingNodeData = findNodeData(draggingNode.data._id, c)!;
      const dropNodeData = findNodeData(dropNode.data._id, c)!;
      // 判断目标节点的父节点是否存在当前拖拽节点，存在则报错
      if (isCyclical(draggingNodeData, dropNodeData)) {
        ibiz.message.error('不能更改父节点，因为这会导致循环！');
        return false;
      }
      const result = c.calcAllowDrop(draggingNodeData, dropNodeData, type);
      return result;
    };

    const allowDrag = (draggingNode: IData): boolean => {
      if (draggingNode.data?._load_more) {
        return false;
      }
      const nodeData = findNodeData(draggingNode.data._id, c)!;
      if (!nodeData) {
        return false;
      }
      return c.calcAllowDrag(nodeData);
    };

    let isTootRefresh = false;

    /**
     * 处理拖入完成事件
     * @param {IData} draggingNode
     * @param {IData} dropNode
     * @param {NodeDropType} dropType
     */
    const handleDrop = async (
      draggingNode: IData,
      dropNode: IData,
      dropType: NodeDropType,
    ): Promise<void> => {
      // 位置没变则不执行拖拽
      if (draggingNode.parent.id === dropNode.id) return;

      const type = formatNodeDropType(dropType);
      const draggingNodeData = findNodeData(draggingNode.data._id, c)!;
      const dropNodeData = findNodeData(dropNode.data._id, c)!;
      await c.onNodeDrop(draggingNodeData, dropNodeData, type);

      const dropNodeModel = c.getNodeModel(dropNodeData._nodeId)!;
      // 判断是否为根节点，如果拖入节点是根节点则会初始化所有数据，组件内也需要调用刷新，初始化所有数据
      if (dropNodeModel?.rootNode) {
        isTootRefresh = true;
        return;
      }
      dropNodeData._children = undefined;
      treeRef.value?.dropAfterActive(draggingNode, dropNode);
    };

    const updateNodeExpand = (data: IData, expanded: boolean): void => {
      const nodeData = findNodeData(data._id, c);
      if (!nodeData) {
        return;
      }
      c.onExpandChange(nodeData as ITreeNodeData, expanded);
    };

    c.evt.on('onLoadSuccess', () => {
      if (isTootRefresh) {
        isTootRefresh = false;
        treeRef.value.refresh();
      }
    });

    return {
      c,
      ns,
      treeRef,
      onNodeClick,
      onNodeDbClick,
      loadData,
      findNodeData,
      allowDrop,
      allowDrag,
      handleDrop,
      onPanelItemEvent,
      updateNodeExpand,
    };
  },
  render() {
    const slots: IData = {
      searchbar: () => <sapn></sapn>,
    };
    const key = this.c.controlPanel ? 'tree' : 'default';
    slots[key] = (): any => {
      if (this.c.state.isLoaded) {
        return (
          <div class={this.ns.b()}>
            <RecursiveTree
              ref='treeRef'
              load={this.loadData}
              allow-drop={this.allowDrop}
              allow-drag={this.allowDrag}
              onNodeExpand={({ data }: IData) => {
                this.updateNodeExpand(data, true);
              }}
              onNodeCollapse={({ data }: IData) => {
                this.updateNodeExpand(data, false);
              }}
              onNodeDrop={this.handleDrop}
            >
              {{
                default: (_node: IData) => {
                  const { data } = _node;
                  const nodeData = this.findNodeData(data._id, this.c)!;
                  if (!nodeData) {
                    return null;
                  }
                  const nodeModel = this.c.getNodeModel(nodeData._nodeId)!;

                  const layoutPanel = getNodeControlPanel(nodeModel);

                  let content;
                  if (layoutPanel) {
                    content = (
                      <iBizControlShell
                        data={nodeData}
                        modelData={layoutPanel}
                        context={this.c.context}
                        params={this.c.params}
                        onPanelItemEvent={(_args: IData) =>
                          this.onPanelItemEvent(_args, _node)
                        }
                      ></iBizControlShell>
                    );
                  } else {
                    content = [
                      nodeData._textHtml ? (
                        <span
                          class={this.ns.be('node', 'label')}
                          v-html={nodeData._textHtml}
                        ></span>
                      ) : (
                        <span class={this.ns.be('node', 'label')}>
                          {nodeData._text}
                        </span>
                      ),
                    ];
                  }

                  return (
                    <div
                      class={this.ns.b('node')}
                      onDblclick={evt => this.onNodeDbClick(nodeData, evt)}
                      onClick={evt => this.onNodeClick(nodeData, data, evt)}
                    >
                      {content}
                    </div>
                  );
                },
                active: (_node: IData) => {
                  const { data } = _node;
                  const nodeData = this.findNodeData(data._id, this.c)!;
                  if (!nodeData) {
                    return null;
                  }
                  const nodeModel = this.c.getNodeModel(nodeData._nodeId)!;
                  const layoutPanel = getActiveNodeControlPanel(nodeModel);

                  if (layoutPanel) {
                    return (
                      <iBizControlShell
                        class={this.ns.b('node-active')}
                        data={nodeData}
                        modelData={layoutPanel}
                        context={this.c.context}
                        params={this.c.params}
                      ></iBizControlShell>
                    );
                  }
                  return '';
                },
              }}
            </RecursiveTree>
          </div>
        );
      }
    };

    return (
      <iBizControlNavigation controller={this.c}>
        <iBizControlBase
          ref={'treeviewRef'}
          controller={this.c}
          v-loading={this.c.state.isLoading}
        >
          {slots}
        </iBizControlBase>
      </iBizControlNavigation>
    );
  },
});
